﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Crank.Core
{
    public abstract class ParserBase
    {
        private ITokenProvider source;

        public void Parse(ITokenProvider source)
        {
            this.source = source;
            ParseProgram();
        }

        private void ParseProgram()
        {
            while (!source.CurrentToken.Is(TokenType.EOF))
                ReadExpression();
            Finished();
        }

        private void ReadExpression()
        {
            StartExpression();
            ReadTableDefList();
            if (source.CurrentToken.Is(TokenType.Operator))
            {
                ReadOperatorExpression();
                ReadExpression();
            }
        }

        private void ReadOperatorExpression()
        {
            MentionLink(source.Eat(TokenType.Operator).Text);
            if (source.TryEat(TokenType.OpenBrace))
            {
                ReadAttribute();
                source.Eat(TokenType.CloseBrace);
            }
        }

        private void ReadTableDefList()
        {
            MentionEntity(ReadIdentifier());
            while (source.TryEat(TokenType.Comma))
                MentionEntity(ReadIdentifier());
            while (source.TryEat(TokenType.HasArrow))
            {
                source.Eat(TokenType.OpenBrace);
                ReadAttributeList();
                source.Eat(TokenType.CloseBrace);
                ReadAttributeType();
            }
        }

        private void ReadAttributeType()
        {
            string typename = ReadIdentifier();
            string sizeModifier = "";
            if (source.TryEat(TokenType.OpenBracket))
            {
                sizeModifier = String.Format("({0})", source.Eat(TokenType.Number).Text);
                source.Eat(TokenType.CloseBracket);
            }
            MentionAttributeType(String.Format("{0}{1}", typename, sizeModifier));
        }

        private void ReadAttributeList()
        {
            ReadAttribute();
            while (source.TryEat(TokenType.Comma))
                ReadAttribute();
        }

        private void ReadAttribute()
        {
            bool isKey = source.TryEat(TokenType.KeyAdapter);
            bool isAuto = source.TryEat(TokenType.KeyAdapter);
            string name = ReadIdentifier();
            bool? isRequired = null;
            if (source.CurrentToken.Is(TokenType.SpecifityAdapter))
            {
                isRequired = source.Eat(TokenType.SpecifityAdapter).Text == "!";
            }
            MentionAttribute(name, isRequired, isKey, isAuto);
        }

        private string ReadIdentifier()
        {
            return source.Eat(TokenType.Identifier).Text;
        }

        protected abstract void StartExpression();
        protected abstract void MentionEntity(string entity);
        protected abstract void MentionAttribute(string attribute, bool? isRequired, bool isKey, bool isAuto);
        protected abstract void MentionAttributeType(string type);
        protected abstract void MentionLink(string type);
        protected abstract void Finished();
    }
}
